package kr.co.sevencore.blefotalib; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; /** * BflFwUploadService.java * BLE FOTA Library Firmware Upload Service. * This service executes Bluetooth properties of BLE FOTA. * * 2015 SEVENCORE Co., Ltd. * * @author Jungwoo Park * @version 1.0.0 * @since 2015-04-08 * @see kr.co.sevencore.blefotalib.BflFwUploader * @see kr.co.sevencore.blefotalib.IBflFwUploadSvc * @see kr.co.sevencore.blefotalib.BflAttributes */ public class BflFwUploadService extends Service { private final static String BLE_FOTA_TAG = BflFwUploadService.class.getSimpleName(); private BluetoothManager mBflBluetoothManager; private BluetoothAdapter mBflBluetoothAdapter; private BluetoothGatt mBflBluetoothGatt; private ArrayList<ArrayList<BluetoothGattCharacteristic>> mBflGattCharacteristics; private BluetoothGattCharacteristic mNotifyCharacteristic; private String mBleDeviceAddress; private Context mContext; // Reserved for immortal background (device scanning) service. /*private static PowerManager sPowerManager; private static PowerManager.WakeLock sCpuWakeLock = null;*/ private boolean mContinuousWriteFlag = false; // When a GATT operation completed successfully, client is able to write continuously. private boolean mWritableNewVersionFlag = false; // Writable firmware new version flag - true: Write, false: Read. private boolean mWritableSeqNumFlag = false; // Writable sequence number flag - true: Write, false: Read. private boolean mReadableDataChkFlag = true; // Readable firmware data check flag - true: Read, false: Notify. private boolean mReadableStatusFlag = true; // Readable firmware status flag - true: Read, false: Notify. private final int VERSION_LENGTH = 8; // Length of version information. public static int sLeftConnCnt = 0; private static boolean sConnCheck = true; public final static int PURE_EACH_CONN_DATA_SIZE = 509; // Maximum firmware data size of each connection event: 509 bytes. public final static int EACH_CONN_DATA_SIZE = 512; // Maximum data size of each connection event: 512 bytes. public final static int EACH_CONN_DATA_INFO = 3; // Sequence number & data size inforamtion of each connection event. public final static String ERROR_LOST_GATT = "kr.co.sevencore.ble.fota.lib.upload.ERROR_LOST_GATT"; public final static String ERROR_LOST_DEVICE_INFORMATION = "kr.co.sevencore.ble.fota.lib.upload.ERROR_LOST_DEVICE_INFORMATION"; public final static String ACTION_GATT_CONNECTING = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_CONNECTING"; public final static String ACTION_GATT_CONNECTED = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTING = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_DISCONNECTING"; public final static String ACTION_GATT_DISCONNECTED = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_GATT_DATA_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_GATT_DATA_AVAILABLE"; public final static String ACTION_FIRMWARE_CURRENT_VERSION_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_VERSION_AVAILABLE"; public final static String ACTION_FIRMWARE_NEW_VERSION_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_NEW_VERSION_AVAILABLE"; public final static String ACTION_SEQUENCE_NUMBER_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_SEQUENCE_NUMBER_AVAILABLE"; public final static String ACTION_FIRMWARE_DATA_CHECK_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_DATA_CHECK_AVAILABLE"; public final static String ACTION_FIRMWARE_STATUS_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_STATUS_AVAILABLE"; public final static String ACTION_MANUFACTURER_NAME_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_MANUFACTURER_NAME_AVAILABLE"; public final static String ACTION_MODEL_NUMBER_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_MODEL_NUMBER_AVAILABLE"; public final static String ACTION_SERIAL_NUMBER_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_SERIAL_NUMBER_AVAILABLE"; public final static String ACTION_DATA_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_DATA_AVAILABLE"; public final static String ACTION_FIRMWARE_NEW_VERSION_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_NEW_VERSION_WRITABLE"; public final static String ACTION_FIRMWARE_DATA_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_DATA_WRITABLE"; public final static String ACTION_SEQUENCE_NUMBER_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_SEQUENCE_NUMBER_WRITABLE"; public final static String ACTION_CHECKSUM_DATA_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_CHECKSUM_DATA_WRITABLE"; public final static String ACTION_FIRMWARE_UPGRADE_TYPE_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_FIRMWARE_UPGRADE_TYPE_WRITABLE"; public final static String ACTION_RESET_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_RESET_WRITABLE"; public final static String ACTION_DATA_WRITABLE = "kr.co.sevencore.ble.fota.lib.upload.ACTION_DATA_WRITABLE"; public final static String GATT_SERVICE_DATA_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.GATT_SERVICE_DATA_AVAILABLE"; public final static String GATT_CHARACTERISTIC_DATA_AVAILABLE = "kr.co.sevencore.ble.fota.lib.upload.GATT_CHARACTERISTIC_DATA_AVAILABLE"; public final static String EXTRA_DATA = "kr.co.sevencore.ble.fota.lib.upload.EXTRA_DATA"; // Firmware upgrade profile attribute UUIDs. public final static UUID UUID_FIRMWARE_VERSION = UUID.fromString(BflAttributes.FIRMWARE_VERSION); public final static UUID UUID_FIRMWARE_NEW_VERSION = UUID.fromString(BflAttributes.FIRMWARE_NEW_VERSION); public final static UUID UUID_FIRMWARE_DATA = UUID.fromString(BflAttributes.FIRMWARE_DATA); public final static UUID UUID_SEQUENCE_NUMBER = UUID.fromString(BflAttributes.SEQUENCE_NUMBER); public final static UUID UUID_CHECKSUM_DATA = UUID.fromString(BflAttributes.CHECKSUM_DATA); public final static UUID UUID_FIRMWARE_DATA_CHECK = UUID.fromString(BflAttributes.FIRMWARE_DATA_CHECK); public final static UUID UUID_FIRMWARE_UPGRADE_TYPE = UUID.fromString(BflAttributes.FIRMWARE_UPGRADE_TYPE); public final static UUID UUID_FIRMWARE_STATUS = UUID.fromString(BflAttributes.FIRMWARE_STATUS); public final static UUID UUID_RESET = UUID.fromString(BflAttributes.RESET); public final static UUID UUID_MANUFACTURER_NAME = UUID.fromString(BflAttributes.MANUFACTURER_NAME); public final static UUID UUID_MODEL_NUMBER = UUID.fromString(BflAttributes.MODEL_NUMBER); public final static UUID UUID_SERIAL_NUMBER = UUID.fromString(BflAttributes.SERIAL_NUMBER); public BflFwUploadService() {} public BflFwUploadService(Context context) { //mContext = context; } @Override public IBinder onBind(Intent intent) { return mBflUploadBinder; } IBflFwUploadSvc.Stub mBflUploadBinder = new IBflFwUploadSvc.Stub() { /** * Initializes a reference to the local Bluetooth adapter. * * @return Return true if the initialization is successful. * @throws RemoteException */ @Override public boolean initUploader() throws RemoteException { return initialize(); } /** * Create connection with the BLE device. * * @param address is the BLE device MAC address to be connected. * @return is a result of creating connection with the BLE device. * @throws RemoteException */ @Override public boolean connect(final String address) throws RemoteException { return createConnection(address); } /** * Disconnect an existing connection or cancel a pending connection. * The disconnection result is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int} * callback. * * @throws RemoteException */ @Override public void disconnect() throws RemoteException{ if (mBflBluetoothAdapter == null || mBflBluetoothGatt == null) { return; } mBflBluetoothGatt.disconnect(); } /** * Return system resources. * After using a given BLE device, * the app must call this method to ensure resources are released properly. * * @throws RemoteException */ @Override public void close() throws RemoteException { if (mBflBluetoothGatt == null) { return; } mBflBluetoothGatt.close(); mBflBluetoothGatt = null; } /** * Update GATT services. * * @throws RemoteException */ @Override public void updateGatt () throws RemoteException{ updateGattServices(getSupportedGattServices()); } /** * Check BLE property. * * @param serviceIdx is the service index of the FOTA profile. * @param characteristicIdx is the characteristic index of the FOTA profile. * @return false, if mBflGattCharacteristics is not available. * @throws RemoteException */ @Override public boolean checkProperty(int serviceIdx, int characteristicIdx) throws RemoteException { if (mBflGattCharacteristics != null) { final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics.get(serviceIdx).get(characteristicIdx); final int characteristicProperty = characteristic.getProperties(); if ((characteristicProperty | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { // READ property: 0X00000002 // If there is an active notification on a characteristic, // clear it first so it doesn't update the data field on the user interface. if (mNotifyCharacteristic != null) { setCharacteristicNotification(mNotifyCharacteristic, false); mNotifyCharacteristic = null; Log.i(BLE_FOTA_TAG, "READ Characteristic property: " + characteristicProperty); } writeBflCharacteristic(characteristic); } if ((characteristicProperty | BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) { // WRITE property: 0X00000008 // If there is an active notification on a characteristic, // clear it first so it doesn't update the data field on the user interface. if (mNotifyCharacteristic != null) { setCharacteristicNotification(mNotifyCharacteristic, false); mNotifyCharacteristic = null; Log.i(BLE_FOTA_TAG, "WRITE Characteristic property: " + characteristicProperty); } writeBflCharacteristic(characteristic); } if ((characteristicProperty | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { // NOTIFY property: 0X00000010 mNotifyCharacteristic = characteristic; setCharacteristicNotification(characteristic, true); Log.i(BLE_FOTA_TAG, "NOTIFICATION Characteristic property: " + characteristicProperty); } return true; } return false; } /** * Read request for FOTA profile execution. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA services. * @throws RemoteException */ @Override public void executeReadCharacteristic(int serviceIdx, int characteristicIdx) throws RemoteException { if (mBflGattCharacteristics == null) { return; } try { final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); readBflCharacteristic(characteristic); } catch (Exception e) { e.printStackTrace(); } } /** * Write request for firmware version information of new firmware due to be upgraded. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param firmwareVersion The Firmware Version is new one to write. * @throws RemoteException */ @Override public void executeWriteFirmwareNewVersion(int serviceIdx, int characteristicIdx, String firmwareVersion) throws RemoteException { if (mBflGattCharacteristics == null) { return; } mWritableNewVersionFlag = true; mWritableSeqNumFlag = false; final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); byte[] byteData = firmwareVersion.getBytes(); characteristic.setValue(byteData); writeBflCharacteristic(characteristic); } /** * Write request for firmware data of new version. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param filePath is the location of the firmware data. * @param sequenceNumber is the size related sequence number information of the firmware data to be transmitted. */ @Override public void executeWriteFirmwareData(int serviceIdx, int characteristicIdx, String filePath, int sequenceNumber) throws RemoteException { if (mBflGattCharacteristics == null) { return; } final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); File binFile = new File(filePath); long length = binFile.length(); byte[] byteData = getByteFromFile(binFile, length); int checkedSeqNumber = checkNegative(sequenceNumber); SplitBytesThread splitBytesThread = new SplitBytesThread(byteData, length, checkedSeqNumber, characteristic); splitBytesThread.start(); } /** * Sequence Number of Firmware Data Write Request. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param index is a sequence number of the firmware data. * @throws RemoteException */ @Override public void executeWriteSequenceNumber(int serviceIdx, int characteristicIdx, int index) throws RemoteException { if (mBflGattCharacteristics == null) { return; } mWritableSeqNumFlag = true; final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); byte[] byteData = new byte[1]; byteData[0] = (byte) index; characteristic.setValue(byteData); writeBflCharacteristic(characteristic); } /** * Checksum Data of Firmware Data. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param filePath is the location of the firmware data. * @throws RemoteException */ @Override public void executeWriteChecksumData(int serviceIdx, int characteristicIdx, String filePath) throws RemoteException { if (mBflGattCharacteristics == null) { return; } final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); File binFile = new File(filePath); long length = binFile.length(); try { byte[] byteData = makeChecksum(binFile, length); characteristic.setValue(byteData); writeBflCharacteristic(characteristic); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } /** * Set firmware upgrade type of the target device. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param typeFlag is thr flag of firmware upgrade type. * @throws RemoteException */ @Override public void executeWriteFirmwareUpgradeType(int serviceIdx, int characteristicIdx, byte typeFlag) throws RemoteException { if (mBflGattCharacteristics == null) { return; } final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); byte[] byteData = new byte[1]; byteData[0] = typeFlag; characteristic.setValue(byteData); writeBflCharacteristic(characteristic); } /** * Reset the target device. * * @param serviceIdx is FOTA profile service index. * @param characteristicIdx is a characteristic index of FOTA service. * @param resetFlag is the flag of rest. * @throws RemoteException */ @Override public void executeWriteReset(int serviceIdx, int characteristicIdx, byte resetFlag) throws RemoteException { if (mBflGattCharacteristics == null) { return; } final BluetoothGattCharacteristic characteristic = mBflGattCharacteristics. get(serviceIdx).get(characteristicIdx); byte[] byteData = new byte[1]; byteData[0] = resetFlag; characteristic.setValue(byteData); writeBflCharacteristic(characteristic); } @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) {} }; @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } /** * Initializes a reference to the local Bluetooth adapter. * * @return Return true if the initialization is successful. */ private boolean initialize() { // For API level 18 and above, get a reference to BluetoothAdapter through BluetoothManager. if (mBflBluetoothManager == null) { mBflBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBflBluetoothManager == null) { Log.e(BLE_FOTA_TAG, "Unable to initialize BluetoothManager."); return false; } } mBflBluetoothAdapter = mBflBluetoothManager.getAdapter(); if (mBflBluetoothAdapter == null) { Log.e(BLE_FOTA_TAG, "Unable to obtain a BluetoothAdapter."); return false; } return true; } /** * Create connection with the BLE device. * * @param address is the BLE device MAC address to be connected. * @return is a result of creating connection with the BLE device. */ private boolean createConnection(final String address) { if (mBflBluetoothAdapter == null || address == null) { Log.w(BLE_FOTA_TAG, "BluetoothAdapter is not initialized or unspecified address."); return false; } // If the device is previously connected device, try to re-connect, if (mBflBluetoothGatt != null) { if (mBleDeviceAddress != null && address.equals(mBleDeviceAddress)) { Log.i(BLE_FOTA_TAG, "Trying to use an existing Bluetooth GATT for connection."); if (mBflBluetoothGatt.connect()) { Log.i(BLE_FOTA_TAG, "Creating connection with the existing Bluetooth GATT."); return true; } else { return false; } } else { broadcastUpdate(ERROR_LOST_DEVICE_INFORMATION); Log.d(BLE_FOTA_TAG, "ADDRESS is NULL"); } } else { broadcastUpdate(ERROR_LOST_GATT); Log.d(BLE_FOTA_TAG, "BLUETOOTH GATT is NULL"); } // Find remote device & make connection state. (Connection to GATT server) final BluetoothDevice device = mBflBluetoothAdapter.getRemoteDevice(address); if(device == null) { Log.w(BLE_FOTA_TAG, "Device not found. Unable to connect."); return false; } mBflGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); mContext = getApplicationContext(); // Set the autoConnect parameter to false. // If you want to directly connect to the device, // parameter: device.connectCatt(CONTEXT, (IF FIND) AUTO_CONNECT, BLUETOOTH_CALLBACK); mBflBluetoothGatt = device.connectGatt(mContext, true, mGattCallback); Log.i(BLE_FOTA_TAG, "Trying to create a new connection."); mBleDeviceAddress = address; return true; } /** * Callback methods for GATT events that the app cares about. * For example, connection change and service discovered. * * onConnectionStateChange: Update connection state. * onServiceDiscovered: New services discovered. * onCharacteristicRead: Callback triggered as a result of a characteristic read operation. * onCharacteristicWrite: Callback triggered as a result of a characteristic write operation. * onCharacteristicChanged: Callback triggered as a result of a remote characteristic notification. */ private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); String intentAction; if (newState == BluetoothProfile.STATE_CONNECTING) { intentAction = ACTION_GATT_CONNECTING; broadcastUpdate(intentAction); Log.i(BLE_FOTA_TAG, "Connecting to GATT server."); } else if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; broadcastUpdate(intentAction); // Attempt to discover services after successful connection. Log.i(BLE_FOTA_TAG, "Connected to GATT server. " + "Attempting to start service discovery: " + mBflBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { intentAction = ACTION_GATT_DISCONNECTING; broadcastUpdate(intentAction); Log.i(BLE_FOTA_TAG, "Disconnecting from GATT server."); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; broadcastUpdate(intentAction); Log.i(BLE_FOTA_TAG, "Disconnected from GATT server."); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); mContinuousWriteFlag = true; Log.i(BLE_FOTA_TAG, "GATT services discovered."); } else { Log.w(BLE_FOTA_TAG, "onServiceDiscovered received: " + status); /*try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } mBflBluetoothGatt.discoverServices();*/ mBflBluetoothAdapter.disable(); Timer initAdapterTimer = new Timer(); initAdapterTimer.schedule(new TimerTask() { @Override public void run() { mBflBluetoothAdapter.enable(); } }, 1000); Timer connCtrlTimer = new Timer(); connCtrlTimer.schedule(new TimerTask() { @Override public void run() { initialize(); createConnection(mBleDeviceAddress); } }, 2000); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { if (UUID_FIRMWARE_VERSION.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_CURRENT_VERSION_AVAILABLE, characteristic); } else if (UUID_FIRMWARE_NEW_VERSION.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_NEW_VERSION_AVAILABLE, characteristic); } else if (UUID_SEQUENCE_NUMBER.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_SEQUENCE_NUMBER_AVAILABLE, characteristic); } else if (UUID_FIRMWARE_DATA_CHECK.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_DATA_CHECK_AVAILABLE, characteristic); } else if (UUID_FIRMWARE_STATUS.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_STATUS_AVAILABLE, characteristic); } else if (UUID_MANUFACTURER_NAME.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_MANUFACTURER_NAME_AVAILABLE, characteristic); } else if (UUID_MODEL_NUMBER.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_MODEL_NUMBER_AVAILABLE, characteristic); } else if (UUID_SERIAL_NUMBER.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_SERIAL_NUMBER_AVAILABLE, characteristic); } else { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); Log.i(BLE_FOTA_TAG, "onCharacteristicRead callback."); } } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { if (UUID_FIRMWARE_NEW_VERSION.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_NEW_VERSION_WRITABLE, characteristic); } else if (UUID_FIRMWARE_DATA.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_DATA_WRITABLE, characteristic); } else if (UUID_SEQUENCE_NUMBER.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_SEQUENCE_NUMBER_WRITABLE, characteristic); } else if (UUID_CHECKSUM_DATA.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_CHECKSUM_DATA_WRITABLE, characteristic); } else if (UUID_FIRMWARE_UPGRADE_TYPE.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_FIRMWARE_UPGRADE_TYPE_WRITABLE, characteristic); } else if (UUID_RESET.equals(characteristic.getUuid())) { broadcastUpdate(ACTION_RESET_WRITABLE, characteristic); } else { broadcastUpdate(ACTION_DATA_WRITABLE, characteristic); Log.i(BLE_FOTA_TAG, "onCharacteristicWrite callback."); } mContinuousWriteFlag = true; } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } }; /** * Update GATT services & characteristics to be available. * * @param gattServices GATT services list. * @see kr.co.sevencore.blefotalib.BflAttributes */ private void updateGattServices(List<BluetoothGattService> gattServices) { if (gattServices != null || mContext != null) { String uuid = null; String unknownServiceStr = mContext.getResources().getString(R.string.unknown_service); String unknownServiceCharacteristicStr = mContext.getResources().getString(R.string.unknown_characteristic); ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>(); ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>(); mBflGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); // Loops through available GATT services. for (BluetoothGattService gattService : gattServices) { HashMap<String, String> currentServiceData = new HashMap<String, String>(); uuid = gattService.getUuid().toString(); currentServiceData.put( // Show service name, if the service is defined at BflAttributes of BLE FOTA Library. BflCodeList.LIST_NAME, BflAttributes.lookup(uuid, unknownServiceStr)); currentServiceData.put(BflCodeList.LIST_UUID, uuid); gattServiceData.add(currentServiceData); ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>(); List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); ArrayList<BluetoothGattCharacteristic> characteristics = new ArrayList<BluetoothGattCharacteristic>(); // Loops through available Characteristics. for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { characteristics.add(gattCharacteristic); HashMap<String, String> currentCharacteristicData = new HashMap<String, String>(); uuid = gattCharacteristic.getUuid().toString(); currentCharacteristicData.put( // Show characteristic name, if the characteristic is defined at BflAttributes of BLE FOTA Library. BflCodeList.LIST_NAME, BflAttributes.lookup(uuid, unknownServiceCharacteristicStr)); currentCharacteristicData.put(BflCodeList.LIST_UUID, uuid); gattCharacteristicGroupData.add(currentCharacteristicData); } mBflGattCharacteristics.add(characteristics); gattCharacteristicData.add(gattCharacteristicGroupData); } broadcastUpdate(ACTION_GATT_DATA_AVAILABLE, gattServiceData, gattCharacteristicData); } } /** * Retrieves a list of supported GATT services on the connected device. * This should be invoked only after * {@code BluetoothGatt#discoverServices()} * completes successfully. * * @return A {@code List} of supported services. */ public List<BluetoothGattService> getSupportedGattServices() { if (mBflBluetoothGatt == null) { return null; } // Return a list of GATT services offered by the remote device. return mBflBluetoothGatt.getServices(); } /** * Broadcast update to notify BLE connection status. * * @param action is one of upload service broadcast cations. */ private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); } /** * Broadcast update to notify GATT service list information. * * @param action is one of upload service broadcast cations. * @param gattServiceData is a BLE service list. * @param gattCharacteristicData is a BLE characteristic list. */ private void broadcastUpdate(final String action, final ArrayList gattServiceData, final ArrayList gattCharacteristicData) { final Intent intent = new Intent(action); intent.putExtra(GATT_SERVICE_DATA_AVAILABLE, gattServiceData); intent.putExtra(GATT_CHARACTERISTIC_DATA_AVAILABLE, gattCharacteristicData); sendBroadcast(intent); } /** * Broadcast update to notify FOTA progress information. * * @param action is one of upload service broadcast cations. * @param characteristic Bluetooth GATT characteristic to be executed. */ private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // This is a special handling for the BLE FOTA profile. // Data parsing is carried out as per profile specifications. if (UUID_FIRMWARE_VERSION.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ //Log.d(BLE_FOTA_TAG, "UUID firmware version property value: " + flag); // Flag: Characteristic property - READ: 0x02 if ((flag & 0x02) != 0) { final byte[] firmwareVersionData = characteristic.getValue(); if (firmwareVersionData != null && firmwareVersionData.length > 0) { intent.putExtra(EXTRA_DATA, new String(firmwareVersionData).substring(0, VERSION_LENGTH)); } } } else if (UUID_FIRMWARE_NEW_VERSION.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ | WRITE //Log.d(BLE_FOTA_TAG, "UUID firmware new version property value: " + flag); // Flag: Characteristic property - READ: 0x02 if (((flag & 0x02) != 0) && (!mWritableNewVersionFlag)) { final byte[] firmwareNewVersionData = characteristic.getValue(); if (firmwareNewVersionData != null && firmwareNewVersionData.length > 0) { intent.putExtra(EXTRA_DATA, new String(firmwareNewVersionData).substring(0, VERSION_LENGTH)); } } // Flag: Characteristic property - WRITE: 0x08 if (((flag & 0x08) != 0) && (mWritableNewVersionFlag)) { intent.putExtra(EXTRA_DATA, "FIRMWARE NEW VERSION SET"); } } else if (UUID_FIRMWARE_DATA.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: WRITE //Log.d(BLE_FOTA_TAG, "UUID firmware data property value: " + flag); // Flag: Characteristic property - WRITE: 0x08 if ((flag & 0x08) != 0) { intent.putExtra(EXTRA_DATA, Integer.toString(sLeftConnCnt)); } } else if (UUID_SEQUENCE_NUMBER.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ | WRITE //Log.d(BLE_FOTA_TAG, "UUID sequence number property value: " + flag); // Flag: Characteristic property - READ: 0x02 if (((flag & 0x02) != 0) && (!mWritableSeqNumFlag)) { final byte[] sequenceNumberInfo = characteristic.getValue(); if (sequenceNumberInfo != null && sequenceNumberInfo.length > 0) { final StringBuilder stringBuilder = new StringBuilder(sequenceNumberInfo.length); for (byte byteChar : sequenceNumberInfo) { stringBuilder.append(String.format("%d", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } // Flag: Characteristic property - WRITE: 0x08 if (((flag & 0x08) != 0) && (mWritableSeqNumFlag)) { intent.putExtra(EXTRA_DATA, "TRANSMITTED " + characteristic.getValue().toString() + " DATA"); } } else if (UUID_CHECKSUM_DATA.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: WRITE //Log.d(BLE_FOTA_TAG, "UUID checksum data property value: " + flag); // Flag: Characteristic property - WRITE: 0x08 if ((flag & 0x08) != 0) { intent.putExtra(EXTRA_DATA, "CHECKSUM DATA TRANSMITTED"); } } else if (UUID_FIRMWARE_DATA_CHECK.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: NOTIFY | READ //Log.d(BLE_FOTA_TAG, "UUID firmware data check property value: " + flag); // Flag: Characteristic property - READ: 0x02 if (((flag & 0x02) != 0) && (mReadableDataChkFlag)) { final byte[] firmwareDataCheckData = characteristic.getValue(); if (firmwareDataCheckData != null && firmwareDataCheckData.length > 0) { final StringBuilder stringBuilder = new StringBuilder(firmwareDataCheckData.length); for (byte byteChar : firmwareDataCheckData) { stringBuilder.append(String.format("%d", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } if (!mReadableDataChkFlag) { int format = -1; // Flag: Characteristic property - NOTIFY: 0x10 if ((flag & 0x10) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(BLE_FOTA_TAG, "BLE FOTA property - firmware data check: format UINT16"); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(BLE_FOTA_TAG, "BLE FOTA property - firmware data check: format UINT8"); } final int firmwareDataCheck = characteristic.getIntValue(format, 1); Log.d(BLE_FOTA_TAG, String.format("FIRMWARE DATA CHECK: %d ", firmwareDataCheck)); intent.putExtra(EXTRA_DATA, String.valueOf(firmwareDataCheck)); } } else if (UUID_FIRMWARE_UPGRADE_TYPE.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: WRITE //Log.d(BLE_FOTA_TAG, "UUID firmware upgrade type property value: " + flag); // Flag: Characteristic property - WRITE: 0x08 if ((flag & 0x08) != 0) { intent.putExtra(EXTRA_DATA, "FIRMWARE UPGRADE TYPE APPLIED"); } } else if (UUID_FIRMWARE_STATUS.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: NOTIFY | READ //Log.d(BLE_FOTA_TAG, "UUID firmware status property value: " + flag); // Flag: Characteristic property - READ: 0x02 if (((flag & 0x02) != 0) && (mReadableStatusFlag)) { final byte[] firmwareStatusData = characteristic.getValue(); if (firmwareStatusData != null && firmwareStatusData.length > 0) { final StringBuilder stringBuilder = new StringBuilder(firmwareStatusData.length); for (byte byteChar : firmwareStatusData) { stringBuilder.append(String.format("%d", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } if (!mReadableStatusFlag) { int format = -1; // Flag: Characteristic property - NOTIFY: 0x10 if ((flag & 0x10) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(BLE_FOTA_TAG, "FIRMWARE STATUS: format UINT16"); } final int firmwareStatus = characteristic.getIntValue(format, 1); Log.d(BLE_FOTA_TAG, String.format("FIRMWARE STATUS: %d ", firmwareStatus)); intent.putExtra(EXTRA_DATA, String.valueOf(firmwareStatus)); } } else if (UUID_RESET.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: WRITE //Log.d(BLE_FOTA_TAG, "UUID reset property value: " + flag); // Flag: Characteristic property - WRITE: 0x08 if ((flag & 0x08) != 0) { intent.putExtra(EXTRA_DATA, "RESET THE TARGET DEVICE NOW"); } } else if (UUID_MANUFACTURER_NAME.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ //Log.d(BLE_FOTA_TAG, "UUID manufacturer name property value: " + flag); // Flag: Characteristic property - READ: 0x02 if ((flag & 0x02) != 0) { final byte[] manufacturerName = characteristic.getValue(); if (manufacturerName != null && manufacturerName.length > 0) { final StringBuilder stringBuilder = new StringBuilder(manufacturerName.length); for (byte byteChar : manufacturerName) { stringBuilder.append(String.format("%c", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } } else if (UUID_MODEL_NUMBER.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ //Log.d(BLE_FOTA_TAG, "UUID model number property value: " + flag); // Flag: Characteristic property - READ: 0x02 if ((flag & 0x02) != 0) { final byte[] modelNumber = characteristic.getValue(); if (modelNumber != null && modelNumber.length > 0) { final StringBuilder stringBuilder = new StringBuilder(modelNumber.length); for (byte byteChar : modelNumber) { stringBuilder.append(String.format("%c", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } } else if (UUID_SERIAL_NUMBER.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); // Returns the properties of this characteristic: READ //Log.d(BLE_FOTA_TAG, "UUID serial number property value: " + flag); // Flag: Characteristic property - READ: 0x02 if ((flag & 0x02) != 0) { final byte[] serialNumber = characteristic.getValue(); if (serialNumber != null && serialNumber.length > 0) { final StringBuilder stringBuilder = new StringBuilder(serialNumber.length); for (byte byteChar : serialNumber) { stringBuilder.append(String.format("%c", byteChar)); } intent.putExtra(EXTRA_DATA, stringBuilder.toString()); } } } else { // For all other profiles, writes the data formatted in HEX. final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for (byte byteChar : data) { stringBuilder.append(String.format("%d", byteChar)); } intent.putExtra(EXTRA_DATA, new String(data)); } } sendBroadcast(intent); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_REDELIVER_INTENT; } @Override public void onCreate() { super.onCreate(); // CPU wake lock is used to keep service running. // CPU wake lock is managed by each service(especially scan service) or the main application. /*sPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); sCpuWakeLock = sPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "FIRMWARE_DOWNLOAD"); sCpuWakeLock.acquire();*/ } @Override public void onDestroy() { super.onDestroy(); /*if (sCpuWakeLock != null) { sCpuWakeLock.release(); sCpuWakeLock = null; }*/ } /** * Enables or disables notification on a given characteristic. * * @param characteristic Characteristic to act on. * @param enabled If true, enable notification. False otherwise. * @see kr.co.sevencore.blefotalib.BflAttributes */ public void setCharacteristicNotification (BluetoothGattCharacteristic characteristic, boolean enabled) { // bluetoothAdapter Initialization check for perform fundamental Bluetooth tasks. // bluetoothGatt Public API for the Bluetooth GATT profile. {@code BluetoothGatt} if (mBflBluetoothAdapter == null || mBflBluetoothGatt == null) { Log.w(BLE_FOTA_TAG, "BluetoothAdapter not initialized"); return; } mBflBluetoothGatt.setCharacteristicNotification(characteristic, enabled); // Firmware upgrade notifications - Client characteristic configuration descriptor: Set notification flag. if ((UUID_FIRMWARE_DATA_CHECK.equals(characteristic.getUuid())) && (!mReadableDataChkFlag)) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(BflAttributes.CLIENT_CHARACTERISTIC_CONFIG) ); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBflBluetoothGatt.writeDescriptor(descriptor); //Log.d(BLE_FOTA_TAG, "Notification enabled - Firmware data check."); } if ((UUID_FIRMWARE_STATUS.equals(characteristic.getUuid())) && (!mReadableStatusFlag)) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(BflAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBflBluetoothGatt.writeDescriptor(descriptor); //Log.d(BLE_FOTA_TAG, "Notification enabled - Firmware status."); } } /* * Request a read on a given {@code BluetoothGattCharacteristic}. * The read result is reported asynchronously through the * {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, * android.bluetooth.BluetoothGattCharacteristic, int)} * callback. * * @param characteristic The characteristic to read from. */ private void readBflCharacteristic(BluetoothGattCharacteristic characteristic) { // bluetoothAdapter Initialization check for perform fundamental Bluetooth tasks. // bluetoothGatt Public API for the Bluetooth GATT profile. {@code BluetoothGatt}. if (mBflBluetoothAdapter == null || mBflBluetoothGatt == null) { Log.w(BLE_FOTA_TAG, "BluetoothAdapter is not initialized."); return; } mWritableNewVersionFlag = false; mBflBluetoothGatt.readCharacteristic(characteristic); } /** * Request a write on a given {@code BluetoothGattCharacteristic}. * The write result is reported through the * {@code BluetoothGattCallback#onCharacteristicWrite(android.bluetooth.BluetoothGatt, * android.bluetooth.BluetoothGattCharacteristic, int)} * * @param characteristic The characteristic to write from. */ private void writeBflCharacteristic(BluetoothGattCharacteristic characteristic) { // bluetoothAdapter Initialization check for perform fundamental Bluetooth tasks. // bluetoothGatt Public API for the Bluetooth GATT profile. {@code BluetoothGatt}. if (mBflBluetoothAdapter == null || mBflBluetoothGatt == null) { Log.w(BLE_FOTA_TAG, "BluetoothAdapter is not initialized."); return; } mBflBluetoothGatt.writeCharacteristic(characteristic); } /** * Check a negative parameter to prevent overflow. * * @param sequenceNumber is 1 byte value. * @return Positive sequence number. */ public static int checkNegative(int sequenceNumber) { Log.e(BLE_FOTA_TAG, "Firmware data sequence number overflowed."); if (sequenceNumber < -1) { sequenceNumber += 256; } return sequenceNumber; } /** * Binary firmware data convert into byte code. * If firmware data split into several bytes because of maximum size of each connection event, * insert sequence index number in front of each bytes (actually at bytes[0] ~ bytes[N]) * * @param binData is the binary file of firmware data. * @param length of the firmware data. * @return Btye data converted from firmware data. */ public static byte[] getByteFromFile(File binData, long length) { byte[] bytes = new byte[(int)length]; int offset = 0; int numRead = 0; try { InputStream firmwareInputStream = new FileInputStream(binData); try { while (offset < bytes.length && (numRead = firmwareInputStream.read(bytes, offset, bytes.length - offset)) >= 0) { offset += numRead; } firmwareInputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } int indexStart = 0; int indexEnd = PURE_EACH_CONN_DATA_SIZE; int indexSeqInfo = 0; int pureConnDataCnt = (int) (length / PURE_EACH_CONN_DATA_SIZE); final int pureLastConnSize = (int) (length % PURE_EACH_CONN_DATA_SIZE); final int pureLastConnSizePlusThree = (int) ((length % PURE_EACH_CONN_DATA_SIZE) +3); //Log.d(BLE_FOTA_TAG, "Pure last connection size: " + pureLastConnSize); int pureTotalConnDataCnt; int totalLength; int seqNum; int addedBytesIndex; if(pureLastConnSize != 0) { pureTotalConnDataCnt = (pureConnDataCnt + 1); totalLength = (int) (length + ((pureConnDataCnt + 1) * EACH_CONN_DATA_INFO)); } else { pureTotalConnDataCnt = pureConnDataCnt; totalLength = (int) (length + (pureConnDataCnt * EACH_CONN_DATA_INFO)); } byte[] addedBytes = new byte[totalLength]; //Log.d(BLE_FOTA_TAG, "Total length: " + totalLength); if(pureLastConnSize != 0) { for (int i=0; i<pureTotalConnDataCnt; i++) { seqNum = i; addedBytesIndex = ((i + 1) * EACH_CONN_DATA_INFO); if (i != pureConnDataCnt) { //Log.d(BLE_FOTA_TAG, "Insert index number: " + i + // ", Total count: " + pureTotalConnDataCnt + ", Total length: " + totalLength); for (int j = indexStart; j < indexEnd; j++) { addedBytes[j + addedBytesIndex] = bytes[j]; } addedBytes[indexSeqInfo] |= (byte) (seqNum & 0xFF); addedBytes[indexSeqInfo + 1] |= (byte) ((EACH_CONN_DATA_SIZE & 0xFF00) >> 8); addedBytes[indexSeqInfo + 2] |= (byte) (EACH_CONN_DATA_SIZE & 0xFF); indexStart += PURE_EACH_CONN_DATA_SIZE; indexEnd += PURE_EACH_CONN_DATA_SIZE; indexSeqInfo += EACH_CONN_DATA_SIZE; } else { //Log.d(BLE_FOTA_TAG , "Insert index number else case:: " + i); indexEnd += (pureLastConnSize - PURE_EACH_CONN_DATA_SIZE); for (int j = indexStart; j < indexEnd; j++) { addedBytes[j + addedBytesIndex] = bytes[j]; } addedBytes[indexSeqInfo] |= (byte) (seqNum & 0xFF); addedBytes[indexSeqInfo + 1] |= (byte) ((pureLastConnSizePlusThree & 0xFF00) >> 8); addedBytes[indexSeqInfo + 2] |= (byte) (pureLastConnSizePlusThree & 0xFF); } } } else { for (int i = 0; i < pureTotalConnDataCnt; i++) { seqNum = i; addedBytesIndex = ((i + 1) * EACH_CONN_DATA_INFO); //Log.d(BLE_FOTA_TAG, "Insert index number: " + i); for (int j = indexStart; j < indexEnd; j++) { addedBytes[j + addedBytesIndex] = bytes[j]; } addedBytes[indexSeqInfo] |= (byte) (seqNum & 0xFF); addedBytes[indexSeqInfo+1] |= (byte) ((EACH_CONN_DATA_SIZE & 0xFF00) >> 8); addedBytes[indexSeqInfo+2] |= (byte) (EACH_CONN_DATA_SIZE & 0xFF); indexStart += PURE_EACH_CONN_DATA_SIZE; indexEnd += PURE_EACH_CONN_DATA_SIZE; indexSeqInfo += EACH_CONN_DATA_SIZE; } } //Log.d(BLE_FOTA_TAG, "Input source sequence numbering: " + Arrays.toString(addedBytes)); return addedBytes; } /** * Split thread in bytes to send a firmware data. * * @see kr.co.sevencore.blefotalib.BflFwUploadService */ class SplitBytesThread extends Thread { ArrayList<byte[]> byteList = new ArrayList<byte[]>(); private byte[] byteData; private long length; private int sequenceNum; private BluetoothGattCharacteristic characteristic; public SplitBytesThread(byte[] byteData, long length, int sequenceNum, BluetoothGattCharacteristic characteristic) { byteList = new ArrayList<byte[]>(); this.byteData = byteData; this.length = length; this.sequenceNum = sequenceNum; this.characteristic = characteristic; } public void run() { // connDataCnt + (lastConnData != 0 ? 1 : 0) = TOTAL CONNECTION EVENT // MAXIMUM CONNECTION EVENTS: connDataCnt <= 200 (100KB) //Log.d(BLE_FOTA_TAG, "Sequence number at split bytes thread: " + sequenceNum); int sendConnDataCnt = sequenceNum + 1; int leftLength = (int) (length - (sendConnDataCnt * PURE_EACH_CONN_DATA_SIZE)); int totalLength; int pureConnDataCnt = leftLength / PURE_EACH_CONN_DATA_SIZE; final int pureLastConnSize = leftLength % PURE_EACH_CONN_DATA_SIZE; //Log.d(BLE_FOTA_TAG, "Send connection data count: " + sendConnDataCnt + ", Left length: " + leftLength); if (pureLastConnSize != 0) { sLeftConnCnt = (pureConnDataCnt + 1); totalLength = leftLength + ((pureConnDataCnt + 1) * EACH_CONN_DATA_INFO); } else { sLeftConnCnt = pureConnDataCnt; totalLength = leftLength + (pureConnDataCnt * EACH_CONN_DATA_INFO); } int connDataCnt = totalLength / EACH_CONN_DATA_SIZE; int lastConnSize = totalLength % EACH_CONN_DATA_SIZE; byte[] container; byte[] lastContainer; int startPt = 0; int index = 0; if (sendConnDataCnt != 0) { startPt = sendConnDataCnt * EACH_CONN_DATA_SIZE; //Log.d(BLE_FOTA_TAG, "Start point: " + startPt); } for (int i = 0; i < connDataCnt; i++) { container = new byte[EACH_CONN_DATA_SIZE]; System.arraycopy(byteData, startPt, container, 0, EACH_CONN_DATA_SIZE); byteList.add(container); //Log.d(BLE_FOTA_TAG, "Container - array copy routine: " + container[0]); //Log.d(BLE_FOTA_TAG, "Insert data from \"byteData\" to \"bytesList\" - Source array start point: " + startPt); startPt += EACH_CONN_DATA_SIZE; } //Log.d(BLE_FOTA_TAG, "Write characteristic - Make array list: " + connDataCnt + " columns."); if (lastConnSize != 0) { lastContainer = new byte[lastConnSize]; System.arraycopy(byteData, startPt, lastContainer, 0, lastConnSize); byteList.add(lastContainer); //Log.d(BLE_FOTA_TAG, "Last container - Array copy routine: " + lastContainer[0]); //Log.d(BLE_FOTA_TAG, "Insert data from \"byteData\" to \"byesList\" - Make last column: " // + lastConnSize + " items."); } while (totalLength > 0 && sConnCheck) { if (mContinuousWriteFlag && (totalLength > EACH_CONN_DATA_SIZE)) { //Log.d(BLE_FOTA_TAG, "Write characteristic when GATT success - Index: " // + index + ", Length size: " + totalLength); mContinuousWriteFlag = false; if (sConnCheck && mBflBluetoothGatt != null && byteList != null) { container = byteList.get(index); //Log.d(BLE_FOTA_TAG, "Container [0], [1], [2] value : " // + container[0] + ", " + container[1] + ", " + container[2]); characteristic.setValue(container); try { mBflBluetoothGatt.writeCharacteristic(characteristic); } catch (NullPointerException e) { e.printStackTrace(); } //Log.d(BLE_FOTA_TAG, "Write characteristic executed - Write size: " + container.length); } totalLength -= EACH_CONN_DATA_SIZE; index++; sLeftConnCnt--; } else if (mContinuousWriteFlag && totalLength < EACH_CONN_DATA_SIZE) { //Log.d(BLE_FOTA_TAG, "Write characteristic when GATT success - Index: " // + index + ", Length size: " + totalLength); mContinuousWriteFlag = false; if (sConnCheck && mBflBluetoothGatt != null && byteList != null) { lastContainer = byteList.get(index); //Log.d(BLE_FOTA_TAG, "Last container [0], [1], [2] value: " // + lastContainer[0] + ", " + lastContainer[1] + ", " + lastContainer[2]); characteristic.setValue(lastContainer); try { mBflBluetoothGatt.writeCharacteristic(characteristic); } catch (NullPointerException e) { e.printStackTrace(); } //Log.d(BLE_FOTA_TAG, "Write characteristic executed - Write size: " + lastContainer.length); } totalLength -= lastConnSize; sLeftConnCnt--; } } super.run(); } } /** * SHA-1 Checksum data. * Input file without sequence number. * * @param binData is firmware data. * @param length is firmware data size. * @return Byte type digest message. * @throws NoSuchAlgorithmException */ private byte[] makeChecksum (File binData, long length) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); try { FileInputStream fileInputStream = new FileInputStream(binData); byte[] byteData = new byte[(int) length]; int nRead = 0; try { while ((nRead = fileInputStream.read(byteData)) != -1) { messageDigest.update(byteData, 0, nRead); } } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } byte[] byteMessageDigest = messageDigest.digest(); //Log.d(BLE_FOTA_TAG, "Make checksum - Byte message digest size: " + byteMessageDigest.length); return byteMessageDigest; } }